#ifndef GAMEENGINE_GAMEPLAY_OBJECTS_OBJECT_CONTAINER_H
#define GAMEENGINE_GAMEPLAY_OBJECTS_OBJECT_CONTAINER_H

#include <GameEngine/Gameplay/gameplay_settings.h>

#include <GameEngine/Gameplay/Objects/object.h>
#include <GameEngine/Gameplay/States/lock_state.h>
#include <GameEngine/Core/Classes/rtti.h>

#include <QtCore/QSet>


#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif

class ObjectInventory;

//=================================================================================


//! A container object
/*!
 *  A container object is an entity that can contain any number of inventory items
 *  in its inventory. ObjectContainer objects provide function to add and remove objects
 *  from their inventory - still, client code should make sure that the inventory
 *  object is notified of its change of container to avoid inconsistent object
 *  states.<br>
 *  <br>
 *  For example, the following code will remove the "sword" object from the container,
 *  but leave the sword object unaware of this change:
 *  @code
 *  ObjectContainer* chest = ...;
 *  ObjectInventory* sword = ...; // sword is assumed to be in the chest
 *  chest->remove(sword, false);
 *  @endcode
 *
 *  There are many ways to correctly notify both the container and the object:<br>
 *  <br>
 *  1. Do everything manually:
 *  @code
 *  ObjectContainer* chest = ...;
 *  ObjectInventory* sword = ...; // sword is assumed to be in the chest
 *  chest->remove(sword, false);
 *  sword->set_container(nil, false);
 *  @endcode
 *
 *  2. Use the container automatic notifier:
 *  @code
 *  ObjectContainer* chest = ...;
 *  ObjectInventory* sword = ...; // sword is assumed to be in the chest
 *  chest->remove(sword, true);
 *  @endcode
 *
 *  3. Use the inventory object automatic notifier:
 *  @code
 *  ObjectContainer* chest = ...;
 *  ObjectInventory* sword = ...; // sword is assumed to be in the chest
 *  sword->set_container(nil, true);
 *  @endcode
 *
 *  Any of the above methods will produce the same result, i.e., inform both
 *  the container and the inventory object that their state has changed.
 *
 *  A container may be locked or unlocked - the rules that apply to open the container
 *  are determined by its lock state. Please refer to the LockState documentation for
 *  more information on this subject.
 *
 @todo Implement an optional limited capacity of the container
 @note At the current time, a container may not contain another container. This
       limitation may be overcome in future versions.
 */
class _Gameplay_decl ObjectContainer : public Object {
	GAMEENGINE_DECLARE_RTTI_PARENT(ObjectContainer,Object)
	Q_OBJECT

public:
	//! Typedef describing the content of the inventory
	typedef QSet<ObjectInventory*> InventoryList;

public:
	ObjectContainer();
	ObjectContainer(const ObjectContainer& rhs);
	ObjectContainer& operator=(const ObjectContainer& rhs);
	virtual ~ObjectContainer();

	const InventoryList& inventory() const;

	const LockState& lock_state() const;
	LockState& lock_state();

	virtual bool add(ObjectInventory* object, bool notify_object = true);
	virtual bool remove(ObjectInventory* object, bool notify_object = true);
	bool find(ObjectInventory* object) const;

	int total_weight() const;

signals:
	//! Signal called when an object is added to the container
	void object_added(ObjectInventory* object) const;

	//! Signal called when an object is removed from the container
	void object_removed(ObjectInventory* object) const;

protected:
	//! The list of all objects in the container
	InventoryList inventory_;

	//! The lock state of the container
	LockState lock_state_;
};

//========================================================

#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif

#endif
